Skip to content

Gate motion binary_sensor publish behind a successful Security+2.0 sync (#118)#119

Closed
heythisisnate wants to merge 1 commit into
masterfrom
gate-motion-publish-v2-synced
Closed

Gate motion binary_sensor publish behind a successful Security+2.0 sync (#118)#119
heythisisnate wants to merge 1 commit into
masterfrom
gate-motion-publish-v2-synced

Conversation

@heythisisnate

Copy link
Copy Markdown
Contributor

Addresses action item 3 from #118: gate the ESPHome motion publish behind a successful Security+2.0 sync, rather than binding the gdolib callback straight to publish_state.

Why

Motion is a Security+2.0-only feature — the Security+1.0 protocol has no motion command, and decode_v1_packet() has no motion path. But gdolib's protocol probe can transiently run the Security+2.0 decoder against a Security+1.0 bus (gdo_sync_task() falls back to V2 emulation at gdo.c:911-929 when the V1 handshake doesn't resolve in ~7.5 s), and a corrupt frame read in that window can decode as GDO_CMD_MOTION. That is how a Security+1.0 opener with no motion sensor ended up publishing motion. Full trace in #118.

Downstream, a single publish is unrecoverable: the cloud infers a motion sensor exists the moment the entity leaves its unknown state, so one spurious blip permanently marks a sensor that isn't there — and it self-clears after 3 s, so it looks perfectly innocuous in the web UI.

Before this change there was no gate anywhere between a decoded 0x285 and an MQTT publish: binary_sensor/__init__.py binds the parent callback directly to GDOBinarySensor::publish_state, and GDOBinarySensor is an empty class.

What

  • Track synced_ and protocol_ on GDOComponent, populated from the existing GDO_CB_EVENT_SYNCED handler.
  • set_motion_state() moves out of the header and drops the update (with a warning log) unless we are synced and on Security+2.0.

Behavior by case:

Situation synced_ / protocol_ Motion
Boot, protocol probe in flight false / unset dropped
Security+1.0 sync succeeds true / V1 dropped
Security+2.0 sync succeeds true / V2 published as before

Ordering is safe: in gdo_event_handler, set_protocol_state() is called before set_sync_state() and only when the sync actually succeeded, so protocol_ is never populated from a failed probe.

Scope

This is defense in depth, not the root-cause fix. The parser-side fix belongs in gdolib — filed as konnected-io/gdolib#30 (update_motion_state() has no protocol guard). This PR only ensures the ESPHome layer won't forward a motion state it has no basis to believe. Note it does not address spurious motion on a genuine Security+2.0 opener with no motion sensor — on such a device the gate is open by construction, and that case depends on the gdolib parser hardening.

Only motion is gated. Obstruction, button, motor, light, lock and door all exist in the Security+1.0 protocol and are left alone.

Testing

Compiles clean for garage-door-GDOv2-Q.yaml (ESP32-S3) against the local component tree.

Not yet exercised on hardware — the interesting path needs a Security+1.0 opener whose V1 handshake fails into the V2 probe window, which I can't stage here. The confirming signal on a real device is the new Ignoring motion state ...; not synced on Security+2.0 warning appearing where a Motion: detected would previously have published, correlated with gdolib's secplus V1 panel emulation failed, trying secplus V2 panel emulation.

🤖 Generated with Claude Code

Motion is a Security+2.0-only feature; the Security+1.0 protocol has no
motion command. gdolib's protocol probe can transiently run the Sec+2.0
decoder against a Sec+1.0 bus, where a corrupt frame can decode as
GDO_CMD_MOTION, so a Sec+1.0 opener with no motion sensor can publish
motion (#118, konnected-io/gdolib#30).

Downstream this is unrecoverable: the cloud infers a motion sensor exists
the moment the entity leaves its "unknown" state, so one spurious publish
permanently marks a sensor that isn't there.

Track sync and protocol state on GDOComponent and drop motion updates
unless we are synced on Security+2.0. Defense in depth -- the parser-side
fix belongs in gdolib, but the ESPHome layer should not forward a state it
has no basis to believe.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@heythisisnate

Copy link
Copy Markdown
Contributor Author

Better fix is upstream in konnected-io/gdolib#31
This fix isn't really necessary

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant